package com.keguoyu.easymvp.ioc.origin

import com.android.build.api.transform.Status
import com.android.build.api.transform.TransformInput
import com.android.build.api.transform.TransformOutputProvider
import com.keguoyu.easymvp.ioc.internal.util.loopAllFiles
import com.keguoyu.easymvp.ioc.internal.util.loopAllJars
import kotlinx.coroutines.*
import org.apache.commons.io.FileUtils
import java.io.File
import java.nio.file.Files
import java.nio.file.Paths
import java.util.*
import kotlin.collections.HashSet

class FileCopier(fullChange: Boolean) {

  val copy: (Collection<TransformInput>, TransformOutputProvider) -> Pair<Set<File>, Set<File>>

  init {
    if (fullChange) {
      copy = ::copyAll
    } else {
      copy = ::copyOnDemand
    }
  }

  private fun copyAll(inputs: Collection<TransformInput>,
                      outputProvider: TransformOutputProvider): Pair<Set<File>, Set<File>> {
    // Transform的inputs有两种类型，一种是目录，一种是jar包，要分开遍历
    // gradle 官方说是看文件的 hash 值, 简单做法就是未改变的不拷贝(此时老文件已经有 invoker 修改)
    val copiedFiles = HashSet<File>()
    val copiedJars = HashSet<File>()
    val jobs = LinkedList<Deferred<Unit>>()
    runBlocking {
      inputs.forEach { input ->
        input.loopAllFiles(outputProvider) { fin, fout, status ->
          if (fin.exists()) {
            copiedFiles.add(fout)
            jobs += async(Dispatchers.IO) {
              if (fout.exists()) {
                fout.delete()
              }
              FileUtils.copyFile(fin, fout)
            }
          }
        }
        input.loopAllJars(outputProvider) { fin, fout ->
          if (fin.file.exists()) {
            copiedJars.add(fout)
            jobs += async(Dispatchers.IO) {
              if (fout.exists()) {
                fout.delete()
              }
              FileUtils.copyFile(fin.file, fout)
            }
          }
        }
      }

      jobs.awaitAll()
    }
    return Pair(copiedFiles, copiedJars)
  }

  private fun copyOnDemand(inputs: Collection<TransformInput>,
                           outputProvider: TransformOutputProvider): Pair<Set<File>, Set<File>> {
    // Transform的inputs有两种类型，一种是目录，一种是jar包，要分开遍历
    // gradle 官方说是看文件的 hash 值, 简单做法就是未改变的不拷贝(此时老文件已经有 invoker 修改)
    val copiedFiles = HashSet<File>()
    val copiedJars = HashSet<File>()
    val jobs = LinkedList<Deferred<Unit>>()

    runBlocking {
      inputs.forEach { input ->
        // 必须处理，否则kwai-android里的类无法打到最终的apk中
        input.loopAllFiles(outputProvider)
        task@{ fin, fout, status ->
          if (status == null) {
            return@task
          }
          if (status == Status.REMOVED) {
            async(Dispatchers.IO) {
              if (fout.exists()) {
                fout.delete()
              }
            }
            return@task
          } else if (status == Status.NOTCHANGED && fout.exists()) {
            return@task
          }

          copiedFiles.add(fout)
          jobs += async(Dispatchers.IO) {
            if (fout.exists()) {
              fout.delete()
            }
            FileUtils.copyFile(fin, fout)
          }
          return@task
        }
        // 对类型为jar文件的input进行遍历
        input.loopAllJars(outputProvider)
        task@{ jarInput, fout ->
          if (jarInput.status == Status.REMOVED && fout.exists()) {
            async(Dispatchers.IO) {
              if (fout.exists()) {
                fout.delete()
              }
            }
            return@task
          }
          if (jarInput.status == Status.NOTCHANGED && fout.exists()) {
            return@task
          }
          copiedJars.add(fout)
          jobs += async(Dispatchers.IO) {
            if (fout.exists()) {
              fout.delete()
            }
            Files.copy(jarInput.file.inputStream(), Paths.get(fout.absolutePath))
            Unit
          }
        }

        jobs.awaitAll()
      }
    }
    return Pair(copiedFiles, copiedJars)
  }
}